/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. joyfish add more changes for cdma apn process // joy.big.fish change for D530 172145472@qq.com */ package com.android.internal.telephony.cdma; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ContentResolver; //+ import android.content.ContentValues;//+ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.ContentObserver; //+ import android.database.Cursor; //+ import android.net.ConnectivityManager; import android.net.IConnectivityManager; import android.net.NetworkInfo; import android.net.TrafficStats; import android.net.Uri;//+ import android.net.wifi.WifiManager; import android.os.AsyncResult; import android.os.INetStatService.Stub;//+ import android.os.Handler;//+ import android.os.Message;//+ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.preference.PreferenceManager; import android.provider.Settings; import android.provider.Settings.Secure;//+ import android.provider.Settings.System;//+ import android.provider.Telephony.Carriers;//+ import android.provider.Telephony; //+ import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.telephony.cdma.CdmaCellLocation; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; import com.android.internal.R; //+ import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.DataCallState; import com.android.internal.telephony.DataConnection.FailCause; import com.android.internal.telephony.DataConnection; import com.android.internal.telephony.DataConnectionTracker; import com.android.internal.telephony.EventLogTags; import com.android.internal.telephony.gsm.ApnSetting; import com.android.internal.telephony.gsm.GsmDataConnection;//+ import com.android.internal.telephony.Phone; import com.android.internal.telephony.RetryManager; import com.android.internal.telephony.ServiceStateTracker; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_NUMERIC_HOME_CDMA; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; //+ /** * {@hide} */ public final class CdmaDataConnectionTracker extends DataConnectionTracker { //protected static final boolean DBG = true;//joyfish add for debug 3g protected final String LOG_TAG = "CDMA"; private CDMAPhone mCdmaPhone; //////{+ //private int mDataUpStats = 0; /** * Handles changes to the APN db. */ private class ApnChangeObserver extends ContentObserver { public ApnChangeObserver () { super(mDataConnectionTracker); } @Override public void onChange(boolean selfChange) { sendMessage(obtainMessage(EVENT_APN_CHANGED)); } } /////////} // Indicates baseband will not auto-attach private boolean noAutoAttach = false; //private ApnSetting preferredApn = null; private boolean mIsScreenOn = true; /** Delay between APN attempts */ protected static final int APN_DELAY_MILLIS = 5000; //+ //useful for debugging boolean failNextConnect = false; private boolean mPendingRestartRadio = false; /** * allApns holds all apns for this sim spn, retrieved from * the Carrier DB. * * Create once after simcard info is loaded */ private ArrayList<ApnSetting> allApns = null; //+ /** * waitingApns holds all apns that are waiting to be connected * * It is a subset of allApns and has the same format */ private ArrayList<ApnSetting> waitingApns = null; //+ private int waitingApnsPermanentFailureCountDown = 0; private ApnSetting preferredApn = null;//+ /** * dataConnectionList holds all the Data connection */ private ArrayList<DataConnection> dataConnectionList; /** Currently active CdmaDataConnection */ private CdmaDataConnection mActiveDataConnection; //private static final String PROPERTY_OPERATOR_NUMERIC_HOME_CDMA = "ro.cdma.home.operator.numeric"; private static final int TIME_DELAYED_TO_RESTART_RADIO = SystemProperties.getInt("ro.cdma.timetoradiorestart", 60000); /** * Pool size of CdmaDataConnection objects. */ private static final int DATA_CONNECTION_POOL_SIZE = 1; private static final int POLL_CONNECTION_MILLIS = 5 * 1000; private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.cdma-reconnect"; private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason"; static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn");//+ static final String APN_ID = "apn_id"; //+ private boolean canSetPreferApn = false; //+ /** * Constants for the data connection activity: * physical link down/up */ private static final int DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE = 0; private static final int DATA_CONNECTION_ACTIVE_PH_LINK_DOWN = 1; private static final int DATA_CONNECTION_ACTIVE_PH_LINK_UP = 2; private static final String[] mSupportedApnTypes = { Phone.APN_TYPE_DEFAULT, Phone.APN_TYPE_MMS, Phone.APN_TYPE_DUN, Phone.APN_TYPE_HIPRI }; private static final String[] mDefaultApnTypes = { Phone.APN_TYPE_DEFAULT, Phone.APN_TYPE_MMS, Phone.APN_TYPE_HIPRI }; // if we have no active Apn this is null protected ApnSetting mActiveApn; // Possibly promote to base class, the only difference is // the INTENT_RECONNECT_ALARM action is a different string. // Do consider technology changes if it is promoted. BroadcastReceiver mIntentReceiver = new BroadcastReceiver () { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_SCREEN_ON)) { mIsScreenOn = true; stopNetStatPoll(); startNetStatPoll(); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { mIsScreenOn = false; stopNetStatPoll(); startNetStatPoll(); } else if (action.equals((INTENT_RECONNECT_ALARM))) { Log.d(LOG_TAG, "Data reconnect alarm. Previous state was " + state); String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON); if (state == State.FAILED) { cleanUpConnection(false, reason); } trySetupData(reason); } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { final android.net.NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); mIsWifiConnected = (networkInfo != null && networkInfo.isConnected()); } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; if (!enabled) { // when wifi got disabeled, the NETWORK_STATE_CHANGED_ACTION // quit and wont report disconnected til next enalbing. mIsWifiConnected = false; } } } }; /** Watches for changes to the APN db. */ private ApnChangeObserver apnObserver; //+ /* Constructor */ CdmaDataConnectionTracker(CDMAPhone p) { super(p); mCdmaPhone = p; p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null); p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); p.mRuimRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null); p.mRuimRecords.registerForSMSInitCompleted(this, EVENT_SMS_INIT_COMPLETED, null); //+ p.mCM.registerForNVReady(this, EVENT_NV_READY, null); p.mCM.registerForDataStateChanged (this, EVENT_DATA_STATE_CHANGED, null); p.mCT.registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null); p.mCT.registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null); p.mSST.registerForCdmaDataConnectionAttached(this, EVENT_TRY_SETUP_DATA, null); p.mSST.registerForCdmaDataConnectionDetached(this, EVENT_CDMA_DATA_DETACHED, null); p.mSST.registerForRoamingOn(this, EVENT_ROAMING_ON, null); p.mSST.registerForRoamingOff(this, EVENT_ROAMING_OFF, null); p.mCM.registerForCdmaOtaProvision(this, EVENT_CDMA_OTA_PROVISION, null); //+p.mCM.registerForGpsAltRequest(this, 101, null); //+this.netstat = INetStatService.Stub.asInterface(ServiceManager.getService("netstat")); IntentFilter filter = new IntentFilter(); filter.addAction(INTENT_RECONNECT_ALARM); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); // TODO: Why is this registering the phone as the receiver of the intent // and not its own handler? p.getContext().registerReceiver(mIntentReceiver, filter, null, p); mDataConnectionTracker = this; apnObserver = new ApnChangeObserver(); //+ p.getContext().getContentResolver().registerContentObserver(Telephony.Carriers.CONTENT_URI, true, apnObserver); //+ createAllDataConnectionList(); // This preference tells us 1) initial condition for "dataEnabled", // and 2) whether the RIL will setup the baseband to auto-PS attach. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext()); boolean dataEnabledSetting = true; try { dataEnabledSetting = IConnectivityManager.Stub.asInterface(ServiceManager. getService(Context.CONNECTIVITY_SERVICE)).getMobileDataEnabled(); } catch (Exception e) { // nothing to do - use the old behavior and leave data on } dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(CDMAPhone.DATA_DISABLED_ON_BOOT_KEY, false) && dataEnabledSetting; if (dataEnabled[APN_DEFAULT_ID]) { enabledCount++; //isDefaultNetwork = true;//+ } noAutoAttach = !dataEnabled[APN_DEFAULT_ID]; if (!mRetryMgr.configure(SystemProperties.get("ro.cdma.data_retry_config"))) { if (!mRetryMgr.configure(DEFAULT_DATA_RETRY_CONFIG)) { // Should never happen, log an error and default to a simple linear sequence. Log.e(LOG_TAG, "Could not configure using DEFAULT_DATA_RETRY_CONFIG=" + DEFAULT_DATA_RETRY_CONFIG); mRetryMgr.configure(20, 2000, 1000); } } } public void dispose() { // Unregister from all events phone.mCM.unregisterForAvailable(this); phone.mCM.unregisterForOffOrNotAvailable(this); mCdmaPhone.mRuimRecords.unregisterForRecordsLoaded(this); phone.mCM.unregisterForNVReady(this); phone.mCM.unregisterForDataStateChanged(this); mCdmaPhone.mCT.unregisterForVoiceCallEnded(this); mCdmaPhone.mCT.unregisterForVoiceCallStarted(this); mCdmaPhone.mSST.unregisterForCdmaDataConnectionAttached(this); mCdmaPhone.mSST.unregisterForCdmaDataConnectionDetached(this); mCdmaPhone.mSST.unregisterForRoamingOn(this); mCdmaPhone.mSST.unregisterForRoamingOff(this); phone.mCM.unregisterForCdmaOtaProvision(this); phone.getContext().unregisterReceiver(this.mIntentReceiver); phone.getContext().getContentResolver().unregisterContentObserver(this.apnObserver); //+ destroyAllDataConnectionList(); } protected void finalize() { //if(DBG) Log.d(LOG_TAG, "CdmaDataConnectionTracker finalized"); if (DBG) Log.d("CDMA", "CdmaDataConnectionTracker finalized"); } protected void setState(State s) { log ("setState: " + s); if (state != s) { EventLog.writeEvent(EventLogTags.CDMA_DATA_STATE_CHANGE, state.toString(), s.toString()); state = s; } if (state == State.FAILED) { if (waitingApns != null) waitingApns.clear(); } } /* protected boolean isApnTypeActive(String type) { // TODO: support simultaneous with List instead ///////////////{+ if (Phone.APN_TYPE_DUN.equals(type)) { ApnSetting dunApn = fetchDunApn(); if (dunApn != null) { return ((mActiveApn != null) && (dunApn.toString().equals(mActiveApn.toString()))); } } /////////////////} return mActiveApn != null && mActiveApn.canHandleType(type); } protected boolean isApnTypeAvailable(String type) { ///////////////{+ if (type.equals(Phone.APN_TYPE_DUN)) { return (fetchDunApn() != null); } /////////////////} if (allApns != null) { for (ApnSetting apn : allApns) { if (apn.canHandleType(type)) { return true; } } } return false; } */ /////////////////} public String[] getActiveApnTypes() { String[] result; if (mActiveApn != null) { result = mActiveApn.types; } else { result = new String[1]; result[0] = Phone.APN_TYPE_DEFAULT; } return result; } protected String getActiveApnString() { String result = null; if (mActiveApn != null) { result = mActiveApn.apn; } return result; } /** * The data connection is expected to be setup while device * 1. has ruim card or non-volatile data store * 2. registered to data connection service * 3. user doesn't explicitly disable data service * 4. wifi is not on * * @return false while no data connection if all above requirements are met. */ public boolean isDataConnectionAsDesired() { boolean roaming = phone.getServiceState().getRoaming(); if (((phone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY) || mCdmaPhone.mRuimRecords.getRecordsLoaded()) && (mCdmaPhone.mSST.getCurrentCdmaDataConnectionState() == ServiceState.STATE_IN_SERVICE) && (!roaming || getDataOnRoamingEnabled()) && !mIsWifiConnected ) { return (state == State.CONNECTED); } return true; } @Override protected boolean isApnTypeActive(String type) { // TODO: support simultaneous with List instead //if (Phone.APN_TYPE_DUN.equals(type)) { // ApnSetting dunApn = fetchDunApn(); // if (dunApn != null) { // return ((mActiveApn != null) && (dunApn.toString().equals(mActiveApn.toString()))); // } //} return mActiveApn != null && mActiveApn.canHandleType(type); } @Override protected boolean isApnTypeAvailable(String type) { //if (type.equals(Phone.APN_TYPE_DUN)) { // return (fetchDunApn() != null); //} //if (allApns != null) { // for (ApnSetting apn : allApns) { // if (apn.canHandleType(type)) { // return true; // } // } //} for (String s : mSupportedApnTypes) { if (TextUtils.equals(type, s)) { return true; } } return false; } private boolean isDataAllowed() { boolean roaming = phone.getServiceState().getRoaming(); return getAnyDataEnabled() && (!roaming || getDataOnRoamingEnabled()) && mMasterDataEnabled; } private boolean trySetupData(String reason) { if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason)); if (phone.getSimulatedRadioControl() != null) { // Assume data is connected on the simulator // FIXME this can be improved setState(State.CONNECTED); phone.notifyDataConnection(reason); Log.i(LOG_TAG, "(fix?) We're on the simulator; assuming data is connected"); return true; } int psState = mCdmaPhone.mSST.getCurrentCdmaDataConnectionState(); boolean roaming = phone.getServiceState().getRoaming(); boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState(); if ((state == State.IDLE || state == State.SCANNING) && (psState == ServiceState.STATE_IN_SERVICE) && ((phone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY) || mCdmaPhone.mRuimRecords.getRecordsLoaded()) && (mCdmaPhone.mSST.isConcurrentVoiceAndData() || phone.getState() == Phone.State.IDLE ) && isDataAllowed() && desiredPowerState && !mPendingRestartRadio && !mCdmaPhone.needsOtaServiceProvisioning()) { /////{+ if (state == State.IDLE) { waitingApns = buildWaitingApns(); //waitingApnsPermanentFailureCountDown = waitingApns.size(); if (waitingApns.isEmpty()) { if (DBG) log("No APN found"); notifyNoData(DataConnection.FailCause.MISSING_UNKNOWN_APN); return false; } else { log ("Create from allApns : " + apnListToString(allApns)); } } if (DBG) { log ("Setup waitngApns : " + apnListToString(waitingApns)); } //////////} if (DBG) log("Setup waitingApns :"+apnListToString(waitingApns)); if (DBG) Log.d("CDMA", "updateCurrentCarrierInProvider cdma"); mCdmaPhone.updateCurrentCarrierInProvider(mCdmaPhone.mRuimRecords.getRUIMOperatorNumeric()); return setupData(reason); } else { if (DBG) { log("trySetupData: Not ready for data: " + " dataState=" + state + " PS state=" + psState + " radio state=" + phone.mCM.getRadioState() + " ruim=" + mCdmaPhone.mRuimRecords.getRecordsLoaded() + " concurrentVoice&Data=" + mCdmaPhone.mSST.isConcurrentVoiceAndData() + " phoneState=" + phone.getState() + " dataEnabled=" + getAnyDataEnabled() + " roaming=" + roaming + " dataOnRoamingEnable=" + getDataOnRoamingEnabled() + " desiredPowerState=" + desiredPowerState + " PendingRestartRadio=" + mPendingRestartRadio + " MasterDataEnabled=" + mMasterDataEnabled + " needsOtaServiceProvisioning=" + mCdmaPhone.needsOtaServiceProvisioning()); } return false; } } /** * If tearDown is true, this only tears down a CONNECTED session. Presently, * there is no mechanism for abandoning an INITING/CONNECTING session, * but would likely involve cancelling pending async requests or * setting a flag or new state to ignore them when they came in * @param tearDown true if the underlying DataConnection should be * disconnected. * @param reason reason for the clean up. */ private void cleanUpConnection(boolean tearDown, String reason) { if (DBG) log("cleanUpConnection: reason: " + reason); // Clear the reconnect alarm, if set. if (mReconnectIntent != null) { AlarmManager am = (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); am.cancel(mReconnectIntent); mReconnectIntent = null; } setState(State.DISCONNECTING); // Samsung CDMA devices require this property to be set // so that pppd will be killed to stop 3G data if (SystemProperties.get("ro.ril.samsung_cdma").equals("true")) SystemProperties.set("ril.cdma.data_ready", "false"); boolean notificationDeferred = false; for (DataConnection conn : dataConnectionList) { if(conn != null) { if (tearDown) { if (DBG) log("cleanUpConnection: teardown, call conn.disconnect"); conn.disconnect(obtainMessage(EVENT_DISCONNECT_DONE, reason)); notificationDeferred = true; } else { if (DBG) log("cleanUpConnection: !tearDown, call conn.resetSynchronously"); conn.resetSynchronously(); notificationDeferred = false; } } } stopNetStatPoll(); if (!notificationDeferred) { if (DBG) log("cleanupConnection: !notificationDeferred"); gotoIdleAndNotifyDataConnection(reason); } } //////{+ /** * @param types comma delimited list of APN types * @return array of APN types */ private String[] parseTypes(String types) { String[] result; // If unset, set to DEFAULT. if (types == null || types.equals("")) { result = new String[1]; result[0] = Phone.APN_TYPE_ALL; } else { result = types.split(","); } return result; } private ArrayList<ApnSetting> createApnList(Cursor cursor) { ArrayList<ApnSetting> result = new ArrayList<ApnSetting>(); ApnSetting apnPrefer = null; if (cursor.moveToFirst()) { do { String[] types = parseTypes( cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE))); ApnSetting apn = new ApnSetting( cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)), cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)), cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)), cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)), cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)), cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)), cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC)), cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)), cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)), cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)), cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)), cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)), types, cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)), cursor.getString(cursor.getColumnIndexOrThrow( Telephony.Carriers.ROAMING_PROTOCOL))); Log.d(LOG_TAG, "createApnList:"+apn); if (apnPrefer == null) apnPrefer = apn; result.add(apn); } while (cursor.moveToNext()); } //////////////////////joy fish add patch for issue D/CDMA ( 1346): Get PreferredAPNnull //if ((apnPrefer!=null) && (getPreferredApn() == null)) //{ // setPreferredApn(0); //} return result; } /////////} private CdmaDataConnection findFreeDataConnection() { for (DataConnection connBase : dataConnectionList) { CdmaDataConnection conn = (CdmaDataConnection) connBase; if (conn.isInactive()) { return conn; } } return null; } private boolean setupData(String reason) { ApnSetting apn = getNextApn(); if (apn == null) { log ("setupData fail apn null !!!!"); return false; } CdmaDataConnection conn = findFreeDataConnection(); if (conn == null) { if (DBG) log("setupData: No free CdmaDataConnection found!"); return false; } mActiveApn = apn; mActiveDataConnection = conn; String[] types; if (mRequestedApnType.equals(Phone.APN_TYPE_DUN)) { types = new String[1]; types[0] = Phone.APN_TYPE_DUN; } else { types = mDefaultApnTypes; } ///String[] arrayOfString = new string[1]; //types[0] = "mms"; //mActiveApn = new ApnSetting(0, "", "", "", "", "", "", "", "", "", "", // 0, types, "IP", "IP"); //mActiveApn = new ApnSetting(0, "46003", "ctnet", "ctnet", "", "", "", "", "", "ctnet@mycdma.cn","vnet.mobi", // 2, /*"#777",*/ types, "IP", "IP"); Message msg = obtainMessage(); msg.what = EVENT_DATA_SETUP_COMPLETE; msg.obj = reason; conn.connect(msg, apn); setState(State.INITING); phone.notifyDataConnection(reason); return true; } //////{+ protected String getInterfaceName(String apnType) { if (mActiveDataConnection != null ) //&& // (apnType == null || // (mActiveApn != null && mActiveApn.canHandleType(apnType)))) { return mActiveDataConnection.getInterface(); } return null; } protected String getIpAddress(String apnType) { if (mActiveDataConnection != null ) //&& //(apnType == null || //(mActiveApn != null && mActiveApn.canHandleType(apnType)))) { return mActiveDataConnection.getIpAddress(); } return null; } protected String getGateway(String apnType) { if (mActiveDataConnection != null ) //&& //(apnType == null || //(mActiveApn != null && mActiveApn.canHandleType(apnType)))) { return mActiveDataConnection.getGatewayAddress(); } return null; } protected String[] getDnsServers(String apnType) { if (mActiveDataConnection != null) // && //(apnType == null || //(mActiveApn != null && mActiveApn.canHandleType(apnType)))) { return mActiveDataConnection.getDnsServers(); } return null; } public ArrayList<DataConnection> getAllDataConnections() { return dataConnectionList; } /** * Handles changes to the APN database. */ private void onApnChanged() { boolean isConnected; isConnected = (state != State.IDLE && state != State.FAILED); // The "current" may no longer be valid. MMS depends on this to send properly. //String operator = SystemProperties.get(PROPERTY_OPERATOR_NUMERIC_HOME_CDMA); //mCdmaPhone.mRuimRecords.getRUIMOperatorNumeric(); String operator = mCdmaPhone.mRuimRecords.getRUIMOperatorNumeric(); mCdmaPhone.updateCurrentCarrierInProvider(operator); // TODO: It'd be nice to only do this if the changed entrie(s) // match the current operator. createAllApnList(); if (state != State.DISCONNECTING) { cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED); if (!isConnected) { // reset reconnect timer mRetryMgr.resetRetryCount(); //mReregisterOnReconnectFailure = false; trySetupData(Phone.REASON_APN_CHANGED); } } } //////} private void notifyDefaultData(String reason) { setState(State.CONNECTED); phone.notifyDataConnection(reason); startNetStatPoll(); mRetryMgr.resetRetryCount(); } private void resetPollStats() { txPkts = -1; rxPkts = -1; sentSinceLastRecv = 0; netStatPollPeriod = POLL_NETSTAT_MILLIS; mNoRecvPollCount = 0; } protected void startNetStatPoll() { if (state == State.CONNECTED && netStatPollEnabled == false) { Log.d(LOG_TAG, "[DataConnection] Start poll NetStat"); resetPollStats(); netStatPollEnabled = true; mPollNetStat.run(); } } protected void stopNetStatPoll() { netStatPollEnabled = false; removeCallbacks(mPollNetStat); Log.d(LOG_TAG, "[DataConnection] Stop poll NetStat"); } protected void restartRadio() { if (DBG) log("Cleanup connection and wait " + (TIME_DELAYED_TO_RESTART_RADIO / 1000) + "s to restart radio"); cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF); sendEmptyMessageDelayed(EVENT_RESTART_RADIO, TIME_DELAYED_TO_RESTART_RADIO); mPendingRestartRadio = true; } private Runnable mPollNetStat = new Runnable() { public void run() { long sent, received; long preTxPkts = -1, preRxPkts = -1; Activity newActivity; preTxPkts = txPkts; preRxPkts = rxPkts; txPkts = TrafficStats.getMobileTxPackets(); rxPkts = TrafficStats.getMobileRxPackets(); //Log.d(LOG_TAG, "rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts)); if (netStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) { sent = txPkts - preTxPkts; received = rxPkts - preRxPkts; if ( sent > 0 && received > 0 ) { sentSinceLastRecv = 0; newActivity = Activity.DATAINANDOUT; } else if (sent > 0 && received == 0) { if (phone.getState() == Phone.State.IDLE) { sentSinceLastRecv += sent; } else { sentSinceLastRecv = 0; } newActivity = Activity.DATAOUT; } else if (sent == 0 && received > 0) { sentSinceLastRecv = 0; newActivity = Activity.DATAIN; } else if (sent == 0 && received == 0) { newActivity = (activity == Activity.DORMANT) ? activity : Activity.NONE; } else { sentSinceLastRecv = 0; newActivity = (activity == Activity.DORMANT) ? activity : Activity.NONE; } if (activity != newActivity) { activity = newActivity; phone.notifyDataActivity(); } } if (sentSinceLastRecv >= NUMBER_SENT_PACKETS_OF_HANG) { // Packets sent without ack exceeded threshold. if (mNoRecvPollCount == 0) { EventLog.writeEvent( EventLogTags.PDP_RADIO_RESET_COUNTDOWN_TRIGGERED, sentSinceLastRecv); } if (mNoRecvPollCount < NO_RECV_POLL_LIMIT) { mNoRecvPollCount++; // Slow down the poll interval to let things happen netStatPollPeriod = POLL_NETSTAT_SLOW_MILLIS; } else { if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) + " pkts since last received"); // We've exceeded the threshold. Restart the radio. netStatPollEnabled = false; stopNetStatPoll(); restartRadio(); EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, NO_RECV_POLL_LIMIT); } } else { mNoRecvPollCount = 0; netStatPollPeriod = POLL_NETSTAT_MILLIS; } if (netStatPollEnabled) { mDataConnectionTracker.postDelayed(this, netStatPollPeriod); } } }; /** * Returns true if the last fail cause is something that * seems like it deserves an error notification. * Transient errors are ignored */ private boolean shouldPostNotification(FailCause cause) { return (cause != FailCause.UNKNOWN); } /** * Return true if data connection need to be setup after disconnected due to * reason. * * @param reason the reason why data is disconnected * @return true if try setup data connection is need for this reason */ private boolean retryAfterDisconnected(String reason) { boolean retry = true; if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) { retry = false; } return retry; } private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) { if (state == State.FAILED) { /** * For now With CDMA we never try to reconnect on * error and instead just continue to retry * at the last time until the state is changed. * TODO: Make this configurable? */ int nextReconnectDelay = mRetryMgr.getRetryTimer(); Log.d(LOG_TAG, "Data Connection activate failed. Scheduling next attempt for " + (nextReconnectDelay / 1000) + "s"); AlarmManager am = (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(INTENT_RECONNECT_ALARM); intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason); mReconnectIntent = PendingIntent.getBroadcast( phone.getContext(), 0, intent, 0); am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + nextReconnectDelay, mReconnectIntent); mRetryMgr.increaseRetryCount(); if (!shouldPostNotification(lastFailCauseCode)) { Log.d(LOG_TAG,"NOT Posting Data Connection Unavailable notification " + "-- likely transient error"); } else { notifyNoData(lastFailCauseCode); } } } private void notifyNoData(FailCause lastFailCauseCode) { setState(State.FAILED); } private void gotoIdleAndNotifyDataConnection(String reason) { if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason); setState(State.IDLE); phone.notifyDataConnection(reason); mActiveApn = null; } protected void onRecordsLoaded() { createAllApnList(); //+ if (state == State.FAILED) { cleanUpConnection(false, null); } sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED)); } protected void onNVReady() { if (state == State.FAILED) { cleanUpConnection(false, null); } sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); } /** * @override com.android.internal.telephony.DataConnectionTracker */ @Override protected void onEnableNewApn() { cleanUpConnection(true, Phone.REASON_APN_SWITCHED); } /** * @override com.android.internal.telephony.DataConnectionTracker */ protected boolean onTrySetupData(String reason) { return trySetupData(reason); } /** * @override com.android.internal.telephony.DataConnectionTracker */ protected void onRoamingOff() { trySetupData(Phone.REASON_ROAMING_OFF); } ////////+ protected void onSMSInitCompleted() { log("onSMSInitCompleted,send EVENT_TRY_SETUP_DATA"); if(super.state == com.android.internal.telephony.DataConnectionTracker.State.FAILED) { log("onSMSInitCompleted, clean up connection"); cleanUpConnection(false, null); } sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));//5 } ///////// /** * @override com.android.internal.telephony.DataConnectionTracker */ protected void onRoamingOn() { if (getDataOnRoamingEnabled()) { trySetupData(Phone.REASON_ROAMING_ON); } else { if (DBG) log("Tear down data connection on roaming."); cleanUpConnection(true, Phone.REASON_ROAMING_ON); } } /** * @override com.android.internal.telephony.DataConnectionTracker */ protected void onRadioAvailable() { if (phone.getSimulatedRadioControl() != null) { // Assume data is connected on the simulator // FIXME this can be improved setState(State.CONNECTED); phone.notifyDataConnection(null); Log.i(LOG_TAG, "We're on the simulator; assuming data is connected"); } if (state != State.IDLE) { cleanUpConnection(true, null); } } /** * @override com.android.internal.telephony.DataConnectionTracker */ protected void onRadioOffOrNotAvailable() { mRetryMgr.resetRetryCount(); if (phone.getSimulatedRadioControl() != null) { // Assume data is connected on the simulator // FIXME this can be improved Log.i(LOG_TAG, "We're on the simulator; assuming radio off is meaningless"); } else { if (DBG) log("Radio is off and clean up all connection"); cleanUpConnection(false, Phone.REASON_RADIO_TURNED_OFF); } } /** * @override com.android.internal.telephony.DataConnectionTracker */ protected void onDataSetupComplete(AsyncResult ar) { String reason = null; if (ar.userObj instanceof String) reason = (String) ar.userObj; if (ar.exception == null) { // everything is setup //////////{+ if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { SystemProperties.set("gsm.defaultpdpcontext.active", "true"); if (canSetPreferApn && preferredApn == null) { Log.d(LOG_TAG, "PREFERED APN is null"); preferredApn = mActiveApn; setPreferredApn(preferredApn.id); } } else { SystemProperties.set("gsm.defaultpdpcontext.active", "false"); } ///////////} notifyDefaultData(reason); } else { FailCause cause = (FailCause) (ar.result); if(DBG) log("Data Connection setup failed " + cause); int i = waitingApnsPermanentFailureCountDown; int j; if(cause.isPermanentFail()) j = 1; else j = 0; waitingApnsPermanentFailureCountDown = i - j; waitingApns.remove(0); if (waitingApns.isEmpty()) { if (waitingApnsPermanentFailureCountDown == 0) { notifyNoData(cause); phone.notifyDataConnection("apnFailed"); log("Data Connection setup failed " + cause); } else { startDelayedRetry(cause, reason); } } else { log("onDataSetupComplete: Try next APN"); setState(State.SCANNING); // Wait a bit before trying the next APN, so that // we're not tying up the RIL command channel sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), APN_DELAY_MILLIS); } } } /** * Called when EVENT_DISCONNECT_DONE is received. */ protected void onDisconnectDone(AsyncResult ar) { if(DBG) log("EVENT_DISCONNECT_DONE"); String reason = null; if (ar.userObj instanceof String) { reason = (String) ar.userObj; } setState(State.IDLE); // Since the pending request to turn off or restart radio will be processed here, // remove the pending event to restart radio from the message queue. if (mPendingRestartRadio) removeMessages(EVENT_RESTART_RADIO); // Process the pending request to turn off radio in ServiceStateTracker first. // If radio is turned off in ServiceStateTracker, ignore the pending event to restart radio. CdmaServiceStateTracker ssTracker = mCdmaPhone.mSST; if (ssTracker.processPendingRadioPowerOffAfterDataOff()) { mPendingRestartRadio = false; } else { onRestartRadio(); } phone.notifyDataConnection(reason); mActiveApn = null; if (retryAfterDisconnected(reason)) { trySetupData(reason); } } /** * Called when EVENT_RESET_DONE is received so goto * IDLE state and send notifications to those interested. */ @Override protected void onResetDone(AsyncResult ar) { if (DBG) log("EVENT_RESET_DONE"); String reason = null; if (ar.userObj instanceof String) { reason = (String) ar.userObj; } gotoIdleAndNotifyDataConnection(reason); } /** * @override com.android.internal.telephony.DataConnectionTracker */ protected void onVoiceCallStarted() { if (state == State.CONNECTED && !mCdmaPhone.mSST.isConcurrentVoiceAndData()) { stopNetStatPoll(); phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); } } /** * @override com.android.internal.telephony.DataConnectionTracker */ protected void onVoiceCallEnded() { if (state == State.CONNECTED) { if (!mCdmaPhone.mSST.isConcurrentVoiceAndData()) { startNetStatPoll(); phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); } else { // clean slate after call end. resetPollStats(); } } else { mRetryMgr.resetRetryCount(); // in case data setup was attempted when we were on a voice call trySetupData(Phone.REASON_VOICE_CALL_ENDED); } } /** * @override com.android.internal.telephony.DataConnectionTracker */ protected void onCleanUpConnection(boolean tearDown, String reason) { cleanUpConnection(tearDown, reason); } ///////{+ /* private void createAllApnList() { this.allApns = new ArrayList(); String str1 = this.mCdmaPhone.mRuimRecords.getRUIMOperatorNumeric(); if (str1 != null) { String str2 = "numeric = '" + str1 + "'"; Cursor localCursor = this.phone.getContext().getContentResolver().query(Telephony.Carriers.CONTENT_URI, null, str2, null, null); Log.d("CDMA", "local cursor:" + str2); if (localCursor != null) { if (localCursor.getCount() > 0) this.allApns = createApnList(localCursor); localCursor.close(); } } if (this.allApns.isEmpty()) { log("No APN found for carrier: " + str1); this.PreferredApn = null; notifyNoData(DataConnection.FailCause.MISSING_UNKNOWN_APN); return; } while (true) { this.PreferredApn = getPreferredApn(); Log.d("CDMA", "Get PreferredAPN: " + this.PreferredApn); if ((this.PreferredApn != null) && (!this.PreferredApn.numeric.equals(str1))) { Log.d("CDMA", "Reset PreferredAPN for operator = " + str1); this.PreferredApn = null; setPreferredApn(-1); } if (this.PreferredApn != null) continue; Iterator localIterator = this.allApns.iterator(); if (!localIterator.hasNext()) continue; ApnSetting localApnSetting = (ApnSetting)localIterator.next(); if ((!localApnSetting.canHandleType("default")) || (!localApnSetting.numeric.equals(str1))) break; Log.d("CDMA", "preferredApn is null, we choose a preferred APN:" + localApnSetting); this.PreferredApn = localApnSetting; setPreferredApn(this.PreferredApn.id); } } */ /** * Based on the sim operator numeric, create a list for all possible pdps * with all apns associated with that pdp * * */ private void createAllApnList() { allApns = new ArrayList<ApnSetting>(); //String operator = SystemProperties.get(PROPERTY_OPERATOR_NUMERIC_HOME_CDMA); //mCdmaPhone.mRuimRecords.getRUIMOperatorNumeric(); String operator = mCdmaPhone.mRuimRecords.getRUIMOperatorNumeric(); if (operator != null) { String selection = "numeric = '" + operator + "'"; Cursor cursor = phone.getContext().getContentResolver().query( Telephony.Carriers.CONTENT_URI, null, selection, null, null); if (cursor != null) { if (cursor.getCount() > 0) { allApns = createApnList(cursor); // TODO: Figure out where this fits in. This basically just // writes the pap-secrets file. No longer tied to GsmDataConnection // object. Not used on current platform (no ppp). //GsmDataConnection pdp = pdpList.get(pdp_name); //if (pdp != null && pdp.dataLink != null) { // pdp.dataLink.setPasswordInfo(cursor); //} } cursor.close(); } } if (allApns.isEmpty()) { if (DBG) log("No APN found for carrier: " + operator); preferredApn = null; //notifyNoData(DataConnection.FailCause.MISSING_UNKNOWN_APN); } else { preferredApn = getPreferredApn(); Log.d(LOG_TAG, "Get PreferredAPN" + preferredApn); if (preferredApn != null && !preferredApn.numeric.equals(operator)) { if (DBG) Log.d("CDMA", "Reset PreferredAPN for operator = " + operator); preferredApn = null; setPreferredApn(-1); } /* if (preferredApn == null) { Iterator localIterator = allApns.iterator(); if (localIterator.hasNext()) { ApnSetting localApnSetting = (ApnSetting)localIterator.next(); if ((localApnSetting.canHandleType("default")) && (localApnSetting.numeric.equals(operator))) { Log.d("CDMA", "preferredApn is null, we choose a preferred APN:" + localApnSetting); preferredApn = localApnSetting; setPreferredApn(preferredApn.id); //return; } } } */ } } ///////} private void createAllDataConnectionList() { dataConnectionList = new ArrayList<DataConnection>(); CdmaDataConnection dataConn; for (int i = 0; i < DATA_CONNECTION_POOL_SIZE; i++) { dataConn = CdmaDataConnection.makeDataConnection(mCdmaPhone); dataConnectionList.add(dataConn); } } private void destroyAllDataConnectionList() { if(dataConnectionList != null) { dataConnectionList.removeAll(dataConnectionList); } } private void onCdmaDataDetached() { if (state == State.CONNECTED) { startNetStatPoll(); phone.notifyDataConnection(Phone.REASON_CDMA_DATA_DETACHED); } else { if (state == State.FAILED) { cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED); mRetryMgr.resetRetryCount(); CdmaCellLocation loc = (CdmaCellLocation)(phone.getCellLocation()); EventLog.writeEvent(EventLogTags.CDMA_DATA_SETUP_FAILED, loc != null ? loc.getBaseStationId() : -1, TelephonyManager.getDefault().getNetworkType()); } trySetupData(Phone.REASON_CDMA_DATA_DETACHED); } } private void onCdmaOtaProvision(AsyncResult ar) { if (ar.exception != null) { int [] otaPrivision = (int [])ar.result; if ((otaPrivision != null) && (otaPrivision.length > 1)) { switch (otaPrivision[0]) { case Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED: case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED: mRetryMgr.resetRetryCount(); break; default: break; } } } } //////{+ /** * * @return waitingApns list to be used to create PDP * error when waitingApns.isEmpty() */ private ArrayList<ApnSetting> buildWaitingApns() { ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>(); //String operator = SystemProperties.get(PROPERTY_OPERATOR_NUMERIC_HOME_CDMA); String operator = mCdmaPhone.mRuimRecords.getRUIMOperatorNumeric(); if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT) && canSetPreferApn && (preferredApn!=null)) { if (DBG) Log.i("CDMA", "Preferred APN:" + operator + ":" + this.preferredApn.numeric + ":" + this.preferredApn); //ApnSetting dun = fetchDunApn(); //if (dun != null) apnList.add(dun); if (preferredApn.numeric.equals(operator)) { if (DBG) Log.i("CDMA", "Waiting APN set to preferred APN"+operator); apnList.add(preferredApn); return apnList; } } else { setPreferredApn(-1); preferredApn = null; } /*String operator = mCdmaPhone.mRuimRecords.getRUIMOperatorNumeric(); if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { if (canSetPreferApn && PreferredApn != null) { Log.i(LOG_TAG, "Preferred APN:" + operator + ":" + PreferredApn.numeric + ":" + PreferredApn); if (PreferredApn.numeric.equals(operator)) { Log.i(LOG_TAG, "Waiting APN set to preferred APN"); apnList.add(PreferredApn); return apnList; } else { setPreferredApn(-1); PreferredApn = null; } } } */ if (allApns != null) { for (ApnSetting apn : allApns) { if (apn.canHandleType(mRequestedApnType)) { apnList.add(apn); } } } return apnList; } private void onGetTransDataStatsDone(AsyncResult asyncresult) { if (DBG) Log.i("CDMA", "onGetTransDataStatsDone"); /* if(asyncresult.exception == null) { int ai[] = (int[])(int[])asyncresult.result; if(ai != null) { dispatchDataState(ai[0], mDataUpStats, ai[1], mDataDownStats, mModemModeState); mDataUpStats = ai[0]; mDataDownStats = ai[1]; } } if(mModemModeState == 2) { Message message = obtainMessage(); message.what = 40; //RIL_REQUEST_ANSWER sendMessageDelayed(message, 5000L); }*/ } private void onPollGetTransDataStats() { if (DBG) Log.i("CDMA", "onPollGetTransDataStats"); //if(mModemModeState == 2) // phone.mCM.getDataTransStats(obtainMessage(39)); //RIL_REQUEST_GET_IMEISV } //////////} private void onRestartRadio() { if (mPendingRestartRadio) { Log.d(LOG_TAG, "************TURN OFF RADIO**************"); phone.mCM.setRadioPower(false, null); /* Note: no need to call setRadioPower(true). Assuming the desired * radio power state is still ON (as tracked by ServiceStateTracker), * ServiceStateTracker will call setRadioPower when it receives the * RADIO_STATE_CHANGED notification for the power off. And if the * desired power state has changed in the interim, we don't want to * override it with an unconditional power on. */ mPendingRestartRadio = false; } } private void writeEventLogCdmaDataDrop() { CdmaCellLocation loc = (CdmaCellLocation)(phone.getCellLocation()); EventLog.writeEvent(EventLogTags.CDMA_DATA_DROP, loc != null ? loc.getBaseStationId() : -1, TelephonyManager.getDefault().getNetworkType()); } protected void onDataStateChanged(AsyncResult ar) { ArrayList<DataCallState> dataCallStates = (ArrayList<DataCallState>)(ar.result); if (ar.exception != null) { // This is probably "radio not available" or something // of that sort. If so, the whole connection is going // to come down soon anyway return; } if (state == State.CONNECTED) { boolean isActiveOrDormantConnectionPresent = false; int connectionState = DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE; // Check for an active or dormant connection element in // the DATA_CALL_LIST array for (int index = 0; index < dataCallStates.size(); index++) { connectionState = dataCallStates.get(index).active; if (connectionState != DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) { isActiveOrDormantConnectionPresent = true; break; } } if (!isActiveOrDormantConnectionPresent) { // No active or dormant connection Log.i(LOG_TAG, "onDataStateChanged: No active connection" + "state is CONNECTED, disconnecting/cleanup"); writeEventLogCdmaDataDrop(); cleanUpConnection(true, null); return; } switch (connectionState) { case DATA_CONNECTION_ACTIVE_PH_LINK_UP: Log.v(LOG_TAG, "onDataStateChanged: active=LINK_ACTIVE && CONNECTED, ignore"); activity = Activity.NONE; phone.notifyDataActivity(); startNetStatPoll(); break; case DATA_CONNECTION_ACTIVE_PH_LINK_DOWN: Log.v(LOG_TAG, "onDataStateChanged active=LINK_DOWN && CONNECTED, dormant"); activity = Activity.DORMANT; phone.notifyDataActivity(); stopNetStatPoll(); break; default: Log.v(LOG_TAG, "onDataStateChanged: IGNORE unexpected DataCallState.active=" + connectionState); } } else { // TODO: Do we need to do anything? Log.i(LOG_TAG, "onDataStateChanged: not connected, state=" + state + " ignoring"); } } //////{+ private ApnSetting getNextApn() { ArrayList<ApnSetting> list = waitingApns; ApnSetting apn = null; if (list != null) { if (!list.isEmpty()) { apn = list.get(0); } } return apn; } private String apnListToString (ArrayList<ApnSetting> apns) { StringBuilder result = new StringBuilder(); for (int i = 0, size = apns.size(); i < size; i++) { result.append('[') .append(apns.get(i).toString()) .append(']'); } return result.toString(); } ////////} private void startDelayedRetry(FailCause cause, String reason) { notifyNoData(cause); reconnectAfterFail(cause, reason); } //////{+ private void setPreferredApn(int pos) { if (!canSetPreferApn) { return; } ContentResolver resolver = phone.getContext().getContentResolver(); resolver.delete(PREFERAPN_URI, null, null); if (DBG) Log.i(LOG_TAG, "setPreferredApn:pos=" + pos);//+ if (pos >= 0) { ContentValues values = new ContentValues(); values.put(APN_ID, pos); resolver.insert(PREFERAPN_URI, values); } } private ApnSetting getPreferredApn() { if (allApns.isEmpty()) { return null; } Cursor cursor = phone.getContext().getContentResolver().query( PREFERAPN_URI, new String[] { "_id", "name", "apn" }, null, null, Telephony.Carriers.DEFAULT_SORT_ORDER); if (cursor != null) { canSetPreferApn = true; } else { canSetPreferApn = false; } if (DBG) Log.d(LOG_TAG,"canSetPreferApn="+canSetPreferApn+",count="+cursor.getCount()); if (canSetPreferApn && cursor.getCount() > 0) { int pos; cursor.moveToFirst(); pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)); for(ApnSetting p:allApns) { if (DBG) Log.d(LOG_TAG,"p.id="+p.id+",pos="+pos); if (p.id == pos && p.canHandleType(mRequestedApnType)) { cursor.close(); if (DBG) Log.d(LOG_TAG,"get prefer p.id="+p.id+",pos="+pos); return p; } } } if (cursor != null) { cursor.close(); } return null; } ////////////////} public void handleMessage (Message msg) { if (!phone.mIsTheCurrentActivePhone) { Log.d(LOG_TAG, "Ignore CDMA msgs since CDMA phone is inactive"); return; } switch (msg.what) { case EVENT_RECORDS_LOADED: onRecordsLoaded(); break; case EVENT_NV_READY: if (DBG) log("[CdmaDataConnectionTracker] EVENT_NV_READY"); if (DBG) Log.d(LOG_TAG, "EVENT_NV_READY"); //+ onNVReady(); break; case EVENT_SMS_INIT_COMPLETED: onSMSInitCompleted(); break; case EVENT_CDMA_DATA_DETACHED: if (DBG) log("[CdmaDataConnectionTracker] EVENT_CDMA_DATA_DETACHED"); onCdmaDataDetached(); break; case EVENT_DATA_STATE_CHANGED: onDataStateChanged((AsyncResult) msg.obj); break; case EVENT_APN_CHANGED: //+ if (DBG) log("[CdmaDataConnectionTracker] EVENT_APN_CHANGED"); onApnChanged();//+ break; //case EVENT_START_NETSTAT_POLL: // log("[CdmaDataConnectionTracker] EVENT_START_NETSTAT_POLL"); // break; //case EVENT_START_RECOVERY: // log("[CdmaDataConnectionTracker] EVENT_START_RECOVERY"); // break; case EVENT_CDMA_OTA_PROVISION: onCdmaOtaProvision((AsyncResult) msg.obj); break; case EVENT_RESTART_RADIO: if (DBG) log("EVENT_RESTART_RADIO"); onRestartRadio(); break; case EVENT_GET_DATA_TRANS_STATS_DONE: //39 onGetTransDataStatsDone((AsyncResult)msg.obj); break; case EVENT_POLL_GET_DATA_TRANS_STATS: //40 onPollGetTransDataStats(); break; default: // handle the message in the super class DataConnectionTracker super.handleMessage(msg); break; } } protected void log(String s) { Log.d(LOG_TAG, "[CdmaDataConnectionTracker] " + s); } }